/*
 * Copyright 2008-2015 Arsen Chaloyan
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "whisper_asr.h"
#include "apt_log.h"
#include "apt_string_table.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <curl/curl.h>
#include <apr_file_io.h>
#include <apr_strings.h>

#ifdef _WIN32
#include <windows.h>
#endif

/** WAV file header structure */
#pragma pack(push, 1)
typedef struct wav_header_t {
    char riff[4];           /* "RIFF" */
    int32_t file_size;      /* File size - 8 */
    char wave[4];           /* "WAVE" */
    char fmt[4];            /* "fmt " */
    int32_t fmt_size;       /* Format chunk size (16 for PCM) */
    int16_t audio_format;   /* Audio format (1 for PCM) */
    int16_t num_channels;   /* Number of channels (1 for mono) */
    int32_t sample_rate;    /* Sample rate */
    int32_t byte_rate;      /* Byte rate = sample_rate * num_channels * bits_per_sample/8 */
    int16_t block_align;    /* Block align = num_channels * bits_per_sample/8 */
    int16_t bits_per_sample;/* Bits per sample (16) */
    char data[4];           /* "data" */
    int32_t data_size;      /* Data chunk size */
} wav_header_t;
#pragma pack(pop)

/** Structure for libcurl write callback */
typedef struct curl_response_t {
    char *data;
    size_t size;
    apr_pool_t *pool;
} curl_response_t;

/** libcurl write callback function */
static size_t whisper_curl_write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t realsize = size * nmemb;
    curl_response_t *response = (curl_response_t *)userp;

    char *ptr = apr_palloc(response->pool, response->size + realsize + 1);
    if (response->data) {
        memcpy(ptr, response->data, response->size);
    }
    memcpy(ptr + response->size, contents, realsize);
    response->data = ptr;
    response->size += realsize;
    response->data[response->size] = '\0';

    return realsize;
}

/** Initialize Whisper ASR configuration */
whisper_asr_config_t* whisper_asr_config_create(apr_pool_t *pool)
{
    whisper_asr_config_t *config = apr_palloc(pool, sizeof(whisper_asr_config_t));
    apt_string_assign(&config->api_url, "http://localhost:5000/v1/audio/transcriptions", pool);
    apt_string_assign(&config->auth_token, "123456", pool);
    apt_string_assign(&config->model, "whisper-1", pool);
    apt_string_assign(&config->response_format, "json", pool);

    apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "【5187-ASR】Default config created - API URL: %s", config->api_url.buf);

    return config;
}

/** Load Whisper ASR configuration from file */
apt_bool_t whisper_asr_config_load(const char *config_file, whisper_asr_config_t *config, apr_pool_t *pool)
{
    apr_file_t *file = NULL;
    apr_status_t status;
    char line[1024];
    apt_bool_t result = FALSE;

    /* Try to open configuration file */
    status = apr_file_open(&file, config_file, APR_READ, APR_OS_DEFAULT, pool);
    if (status != APR_SUCCESS) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Failed to open configuration file: %s (status: %d)", config_file, status);
        return FALSE;
    }

    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Loading Whisper ASR configuration from: %s", config_file);

    /* Read configuration file line by line */
    while (apr_file_gets(line, sizeof(line), file) == APR_SUCCESS) {
        char *key = line;
        char *value;

        /* Skip empty lines and comments */
        if (line[0] == '#' || line[0] == ';' || line[0] == '\n' || line[0] == '\0') {
            continue;
        }

        /* Remove trailing newline */
        char *newline = strchr(line, '\n');
        if (newline) *newline = '\0';

        /* Find the equals sign */
        value = strchr(line, '=');
        if (!value) {
            continue;
        }

        *value = '\0'; /* Split key and value */
        value++; /* Move past the equals sign */

        /* Trim whitespace from key and value */
        while (*key == ' ' || *key == '\t') key++;
        while (*value == ' ' || *value == '\t') value++;

        char *end = key + strlen(key) - 1;
        while (end > key && (*end == ' ' || *end == '\t' || *end == '\r')) {
            *end = '\0';
            end--;
        }

        end = value + strlen(value) - 1;
        while (end > value && (*end == ' ' || *end == '\t' || *end == '\r')) {
            *end = '\0';
            end--;
        }

        /* Process configuration parameters */
        if (strcmp(key, "api_url") == 0) {
            apt_string_assign(&config->api_url, value, pool);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Setting api_url: %s (length: %d)", value, (int)strlen(value));
            apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "【5187-ASR】Config api_url after set: %s (length: %d)",
                   config->api_url.buf ? config->api_url.buf : "NULL",
                   config->api_url.length);
        }
        else if (strcmp(key, "auth_token") == 0) {
            apt_string_assign(&config->auth_token, value, pool);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Setting auth_token: %s", value);
        }
        else if (strcmp(key, "model") == 0) {
            apt_string_assign(&config->model, value, pool);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Setting model: %s", value);
        }
        else if (strcmp(key, "response_format") == 0) {
            apt_string_assign(&config->response_format, value, pool);
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Setting response_format: %s", value);
        }
        else {
            apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "【5187-ASR】Ignoring unknown configuration parameter: %s", key);
        }
    }

    apr_file_close(file);

    /* Validate required parameters */
    if (config->api_url.buf == NULL || config->api_url.length == 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Missing required parameter: api_url");
        return FALSE;
    }

    if (config->auth_token.buf == NULL || config->auth_token.length == 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Missing required parameter: auth_token");
        return FALSE;
    }

    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Whisper ASR configuration loaded successfully");
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】API URL: %s", config->api_url.buf);
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Model: %s", config->model.buf);

    return TRUE;
}

/** Test configuration loading */
apt_bool_t whisper_asr_config_test(const char *config_file, apr_pool_t *pool)
{
    whisper_asr_config_t *config = whisper_asr_config_create(pool);
    if (!config) {
        apt_log(APT_LOG_MARK, APT_PRIO_ERROR, "Failed to create test configuration");
        return FALSE;
    }

    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Testing configuration file: %s", config_file);

    if (whisper_asr_config_load(config_file, config, pool)) {
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Configuration test PASSED");
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】API URL: %s", config->api_url.buf);
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Auth Token: %s", config->auth_token.buf);
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Model: %s", config->model.buf);
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Response Format: %s", config->response_format.buf);
        return TRUE;
    } else {
        apt_log(APT_LOG_MARK, APT_PRIO_ERROR, "【5187-ASR】Configuration test FAILED");
        return FALSE;
    }
}

/** Convert PCM file to WAV format */
apt_bool_t pcm_to_wav(const char *pcm_file_path, const char *wav_file_path,
                     int sample_rate, apr_pool_t *pool)
{
    FILE *pcm_file = NULL;
    FILE *wav_file = NULL;
    apt_bool_t status = FALSE;

    pcm_file = fopen(pcm_file_path, "rb");
    if (!pcm_file) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Failed to open PCM file: %s", pcm_file_path);
        return FALSE;
    }

    wav_file = fopen(wav_file_path, "wb");
    if (!wav_file) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Failed to create WAV file: %s", wav_file_path);
        fclose(pcm_file);
        return FALSE;
    }

    /* Get PCM file size */
    fseek(pcm_file, 0, SEEK_END);
    long pcm_size = ftell(pcm_file);
    fseek(pcm_file, 0, SEEK_SET);

    if (pcm_size <= 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】PCM file is empty: %s", pcm_file_path);
        goto cleanup;
    }

    /* Create WAV header */
    wav_header_t header;
    memcpy(header.riff, "RIFF", 4);
    header.file_size = (int32_t)(pcm_size + sizeof(wav_header_t) - 8);
    memcpy(header.wave, "WAVE", 4);
    memcpy(header.fmt, "fmt ", 4);
    header.fmt_size = 16;
    header.audio_format = 1; /* PCM */
    header.num_channels = 1; /* Mono */
    header.sample_rate = (int32_t)sample_rate;
    header.bits_per_sample = 16;
    header.byte_rate = (int32_t)(sample_rate * header.num_channels * header.bits_per_sample / 8);
    header.block_align = (int16_t)(header.num_channels * header.bits_per_sample / 8);
    memcpy(header.data, "data", 4);
    header.data_size = (int32_t)pcm_size;

    /* Write WAV header */
    if (fwrite(&header, sizeof(header), 1, wav_file) != 1) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Failed to write WAV header");
        goto cleanup;
    }

    /* Copy PCM data */
    char buffer[4096];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), pcm_file)) > 0) {
        if (fwrite(buffer, 1, bytes_read, wav_file) != bytes_read) {
            apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Failed to write PCM data to WAV file");
            goto cleanup;
        }
    }

    status = TRUE;
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Successfully converted PCM to WAV: %s -> %s",
            pcm_file_path, wav_file_path);

cleanup:
    if (pcm_file) fclose(pcm_file);
    if (wav_file) fclose(wav_file);
    return status;
}

/** Extract transcription from JSON response */
static apt_bool_t extract_transcription_from_json(const char *json_response, apt_str_t *transcription, apr_pool_t *pool)
{
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Calling Asr API Response: %s", json_response);

    /* More robust JSON parsing */
    const char *text_field = strstr(json_response, "\"text\"");
    if (!text_field) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】No text field found in JSON response");
        return FALSE;
    }

    /* Find the colon after "text" */
    const char *colon = strstr(text_field, ":");
    if (!colon) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Malformed JSON response: no colon after text field");
        return FALSE;
    }

    /* Find the opening quote of the value */
    const char *value_start = strstr(colon, "\"");
    if (!value_start) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Malformed JSON response: no opening quote for text value");
        return FALSE;
    }
    value_start++; /* Skip the opening quote */

    /* Find the closing quote of the value */
    const char *value_end = strstr(value_start, "\"");
    if (!value_end) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Malformed JSON response: no closing quote for text value");
        return FALSE;
    }

    /* Extract the text value */
    apr_size_t text_length = value_end - value_start;
    if (text_length == 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Empty transcription result");
        apt_string_set(transcription, "");
        return TRUE;
    }

    apt_string_assign_n(transcription, value_start, text_length, pool);
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Extracted transcription: %s", transcription->buf);

    return TRUE;
}

/** Call Whisper ASR API to transcribe audio */
apt_bool_t whisper_asr_transcribe(const char *wav_file_path,
                                 whisper_asr_config_t *config,
                                 apt_str_t *transcription,
                                 apr_pool_t *pool)
{
    apt_bool_t status = FALSE;
    CURL *curl = NULL;
    CURLcode res;
    struct curl_httppost *formpost = NULL;
    struct curl_httppost *lastptr = NULL;
    curl_response_t response = {0};
    response.pool = pool;

    /* Debug configuration parameters */
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】ASR Config - API URL: %s (length: %d)",
           config->api_url.buf ? config->api_url.buf : "NULL", config->api_url.length);
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】ASR Config - Auth Token: %s (length: %d)",
           config->auth_token.buf ? config->auth_token.buf : "NULL", config->auth_token.length);
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】ASR Config - Model: %s (length: %d)",
           config->model.buf ? config->model.buf : "NULL", config->model.length);
    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】ASR Config - Response Format: %s (length: %d)",
           config->response_format.buf ? config->response_format.buf : "NULL", config->response_format.length);

    /* Validate configuration */
    if (!config->api_url.buf || config->api_url.length == 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_ERROR, "【5187-ASR】API URL is empty or NULL");
        return FALSE;
    }

    /* Initialize libcurl */
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    if (!curl) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Failed to initialize libcurl");
        goto cleanup;
    }

    /* Set up form data */
    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "file",
                 CURLFORM_FILE, wav_file_path,
                 CURLFORM_END);

    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "model",
                 CURLFORM_COPYCONTENTS, config->model.buf,
                 CURLFORM_END);

    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "response_format",
                 CURLFORM_COPYCONTENTS, config->response_format.buf,
                 CURLFORM_END);

    curl_formadd(&formpost, &lastptr,
                 CURLFORM_COPYNAME, "language",
                 CURLFORM_COPYCONTENTS, "zh",
                 CURLFORM_END);

    /* Set curl options */
    curl_easy_setopt(curl, CURLOPT_URL, config->api_url.buf);
    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, whisper_curl_write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);

    /* Set authorization header */
    struct curl_slist *headers = NULL;
    char auth_header[256];
    snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", config->auth_token.buf);
    headers = curl_slist_append(headers, auth_header);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    /* Set timeout options */
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);

    apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Calling Whisper ASR API: %s", config->api_url.buf);

    /* Perform the request */
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】curl_easy_perform() failed: %s", curl_easy_strerror(res));
        goto cleanup;
    }

    /* Check response code */
    long http_code = 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
    if (http_code != 200) {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】HTTP request failed with code: %ld", http_code);
        if (response.data) {
            apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "【5187-ASR】Response: %s", response.data);
        }
        goto cleanup;
    }

    if (response.data && response.size > 0) {
        apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "【5187-ASR】Whisper ASR response: %s", response.data);

        /* Extract transcription from JSON */
        status = extract_transcription_from_json(response.data, transcription, pool);

        if (status) {
            apt_log(APT_LOG_MARK, APT_PRIO_INFO, "【5187-ASR】Transcription result: %s", transcription->buf);
        }
    } else {
        apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "【5187-ASR】Empty response from Whisper ASR API");
    }

cleanup:
    /* Clean up */
    if (formpost) {
        curl_formfree(formpost);
    }
    if (headers) {
        curl_slist_free_all(headers);
    }
    if (curl) {
        curl_easy_cleanup(curl);
    }
    curl_global_cleanup();

    return status;
}


// 首先定义一个函数来转义字符串中的特殊字符
static char* escape_xml_chars(apr_pool_t *pool, const char *input) {
    if (!input) return NULL;

    // 计算需要的缓冲区大小
    apr_size_t len = strlen(input);
    apr_size_t new_len = len;
    const char *p;

    // 先计算需要转义的字符数量
    for (p = input; *p; p++) {
        if (*p == '<' || *p == '>') {
            new_len += 3; // 每个字符变成4个字符（如 < -> &lt;），所以增加3
        }
    }

    // 如果没有需要转义的字符，直接返回原字符串的副本
    if (new_len == len) {
        return apr_pstrdup(pool, input);
    }

    // 分配新缓冲区
    char *output = (char*)apr_palloc(pool, new_len + 1);
    char *q = output;

    // 进行转义
    for (p = input; *p; p++) {
        if (*p == '<') {
            strcpy(q, "&lt;");
            q += 4;
        } else if (*p == '>') {
            strcpy(q, "&gt;");
            q += 4;
        } else {
            *q++ = *p;
        }
    }
    *q = '\0';

    return output;
}

/** Generate NLSML result from transcription */
apt_bool_t generate_nlsml_result(apt_str_t *channel_id,
                                 const apt_str_t *transcription,
                                 const char *session_info,
                                apt_str_t *nlsml_result,
                                apr_pool_t *pool)
{
    /* Generate NLSML format result */
    char *nlsml;
    if (session_info && strlen(session_info) > 0) {
        nlsml = apr_psprintf(pool,
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<result>\n"
            "  <interpretation confidence=\"1.0\" grammar=\"session:%s\">\n"
            "    <instance>\n"
            "      <id>%s</id>\n"
            "      <nlresult>%s</nlresult>\n"
            "    </instance>\n"
            "    <input mode=\"speech\">%s</input>\n"
            "  </interpretation>\n"
            "</result>",
             escape_xml_chars(pool, session_info), channel_id->buf, transcription->buf, transcription->buf);
    } else {
        nlsml = apr_psprintf(pool,
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            "<result>\n"
            "  <interpretation confidence=\"1.0\">\n"
            "    <instance>\n"
            "      <id>%s</id>\n"
            "      <nlresult>%s</nlresult>\n"
            "    </instance>\n"
            "    <input mode=\"speech\">%s</input>\n"
            "  </interpretation>\n"
            "</result>",
           channel_id->buf, transcription->buf, transcription->buf);
    }

    apt_string_assign(nlsml_result, nlsml, pool);
    return TRUE;
}
